/*
 * Copyright (c) 2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.libermundi.theorcs.chronicles.tapestry.services;

import org.apache.tapestry5.ioc.Messages;
import org.apache.tapestry5.ioc.MethodAdviceReceiver;
import org.apache.tapestry5.ioc.OrderedConfiguration;
import org.apache.tapestry5.ioc.annotations.Advise;
import org.apache.tapestry5.ioc.annotations.InjectService;
import org.apache.tapestry5.plastic.MethodAdvice;
import org.apache.tapestry5.services.ApplicationStateManager;
import org.libermundi.theorcs.chronicles.ChronicleConstants;
import org.libermundi.theorcs.chronicles.model.acls.ChroniclePermission;
import org.libermundi.theorcs.chronicles.model.menu.PersonaSwitchNavMenuItem;
import org.libermundi.theorcs.chronicles.services.PersonaManager;
import org.libermundi.theorcs.chronicles.tapestry.pages.Logout;
import org.libermundi.theorcs.chronicles.tapestry.pages.Profile;
import org.libermundi.theorcs.chronicles.tapestry.pages.admin.Index;
import org.libermundi.theorcs.chronicles.tapestry.pages.message.AddressBook;
import org.libermundi.theorcs.chronicles.tapestry.pages.message.Message;
import org.libermundi.theorcs.chronicles.tapestry.pages.perso.Background;
import org.libermundi.theorcs.chronicles.tapestry.pages.perso.Description;
import org.libermundi.theorcs.chronicles.tapestry.pages.perso.Sheet;
import org.libermundi.theorcs.chronicles.tapestry.pages.perso.Switch;
import org.libermundi.theorcs.chronicles.tapestry.services.impl.AclChronicleContrainMethodAdvice;
import org.libermundi.theorcs.core.model.Node;
import org.libermundi.theorcs.core.model.NodeFactory;
import org.libermundi.theorcs.core.services.StringSource;
import org.libermundi.theorcs.core.tapestry.services.AnnotatedMethodAdvisor;
import org.libermundi.theorcs.core.tapestry.services.AppHelper;
import org.libermundi.theorcs.layout.LayoutConstants;
import org.libermundi.theorcs.layout.model.menu.DynamicListNavMenuItem;
import org.libermundi.theorcs.layout.model.menu.NavMenuItem;
import org.libermundi.theorcs.layout.model.menu.NavMenuItemType;
import org.libermundi.theorcs.layout.model.menu.StaticNavMenuItem;
import org.libermundi.theorcs.layout.services.NavItemSource;
import org.libermundi.theorcs.layout.tapestry.services.LayoutAppHelper;
import org.libermundi.theorcs.layout.tapestry.services.MenuService;
import org.libermundi.theorcs.security.SecurityConstants;
import org.libermundi.theorcs.security.model.impl.AclNodeConstrain;
import org.libermundi.theorcs.security.model.impl.RoleNodeConstrain;
import org.libermundi.theorcs.security.services.SecurityManager;
import org.springframework.security.acls.domain.BasePermission;

/**
 * @author Martin Papy
 *
 */
public class ChronicleMenuModule {
	@SuppressWarnings("rawtypes")
	public static void contributeMenuService(OrderedConfiguration<Node> configuration,
			Messages messages, LayoutAppHelper layoutHelper, AppHelper appHelper,
			@InjectService("FactionNavItemSource") NavItemSource factionServices,
			@InjectService("FactionMenuStringSource") StringSource factionLabel,
			PersonaManager personaManager, SecurityManager securityManager,
			ApplicationStateManager asm) {		

		createTopLeftChronicleMenu(configuration,messages,layoutHelper,appHelper,factionServices,
				factionLabel,personaManager,securityManager,asm);
		
    	createTopRightChronicleMenu(configuration,messages,layoutHelper,appHelper);
    	
    	createChronicleAdminMenu(configuration,messages,layoutHelper,appHelper);
		
	}

	@SuppressWarnings("rawtypes")
	private static void createTopLeftChronicleMenu(
			OrderedConfiguration<Node> configuration, Messages messages,
			LayoutAppHelper layoutHelper, AppHelper appHelper,
			NavItemSource factionServices,
			StringSource factionLabel, PersonaManager personaManager,
			SecurityManager securityManager, ApplicationStateManager asm) {
		// Top Left Menu in Chronicles

		// Add Faction Menu
		Node<NavMenuItem> factions = NodeFactory.getNode(ChronicleConstants.NAV_FACTIONS_ID);
			factions.setDisplayIfEmpty(Boolean.FALSE);
			factions.setData(new DynamicListNavMenuItem(factionServices));
			factions.getData().setLabel(factionLabel);
			factions.getData().setDescription("message:chronicles.mainmenu.factions.hint");
			factions.getData().setCss("icon-random icon-white");
			factions.setParentId(LayoutConstants.CHRO_TOP_LEFT_MENU_ID); // Link to the Main Chronicles Menu (Left)
		
		configuration.add("chro_faction", factions);

    	// Add MyPersona Menu
		Node<NavMenuItem> persona = NodeFactory.getNode(ChronicleConstants.NAV_PERSONA_ID);
			persona.setData(new StaticNavMenuItem(NavMenuItemType.STATIC_LIST));
			persona.getData().setLabel("message:chronicles.mainmenu.persona");
			persona.getData().setDescription("message:chronicles.mainmenu.persona.hint");
			persona.getData().setCss("icon-user icon-white");
			persona.setParentId(LayoutConstants.CHRO_TOP_LEFT_MENU_ID); // Link to the Main Chronicles Menu (Left)
		
		configuration.add("chro_persona", persona,"after:chro_faction");
		
		// Add Menu Items to "My Persona" Menu
		// Add Description Menu
		Node<NavMenuItem> personaDescription = NodeFactory.getNode();
			personaDescription.setData(new StaticNavMenuItem(NavMenuItemType.DEFAULT));
			personaDescription.getData().setMainPageId(Description.PAGE_ID);
			personaDescription.getData().setLabel("message:chronicles.mainmenu.persona.description");
			personaDescription.getData().setDescription("message:chronicles.mainmenu.persona.description.hint");
			personaDescription.getData().setURI(appHelper.getPageURI(Description.class));
			personaDescription.setParentId(ChronicleConstants.NAV_PERSONA_ID); // Link to the "My Persona" Menu

		configuration.add("chro_personaDescription", personaDescription,"after:chro_persona");		
		
		// Add Background Menu
		Node<NavMenuItem> personaBackground = NodeFactory.getNode();
			personaBackground.setData(new StaticNavMenuItem(NavMenuItemType.DEFAULT));
			personaBackground.getData().setMainPageId(Background.PAGE_ID);
			personaBackground.getData().setLabel("message:chronicles.mainmenu.persona.background");
			personaBackground.getData().setDescription("message:chronicles.mainmenu.persona.background.hint");
			personaBackground.getData().setURI(appHelper.getPageURI(Background.class));
			personaBackground.setParentId(ChronicleConstants.NAV_PERSONA_ID); // Link to the "My Persona" Menu
			
		configuration.add("chro_personaBackground", personaBackground,"after:chro_personaDescription");		

		// Add Sheet Menu
		Node<NavMenuItem> personaSheet = NodeFactory.getNode();
			personaSheet.setData(new StaticNavMenuItem(NavMenuItemType.DEFAULT));
			personaSheet.getData().setMainPageId(Sheet.PAGE_ID);
			personaSheet.getData().setLabel("message:chronicles.mainmenu.persona.sheet");
			personaSheet.getData().setDescription("message:chronicles.mainmenu.persona.sheet.hint");
			personaSheet.getData().setURI(appHelper.getPageURI(Sheet.class));
			personaSheet.setParentId(ChronicleConstants.NAV_PERSONA_ID); // Link to the "My Persona" Menu

		configuration.add("chro_personaSheet", personaSheet,"after:chro_personaBackground");		
		
		// Add Switch Menu for Personna
		Node<NavMenuItem> personaSwitch = NodeFactory.getNode();
			personaSwitch.setDisplayIfEmpty(Boolean.FALSE);
			personaSwitch.setData(new PersonaSwitchNavMenuItem(NavMenuItemType.DEFAULT, personaManager, securityManager, asm));
			personaSwitch.getData().setMainPageId(Switch.PAGE_ID);
			personaSwitch.getData().setLabel("message:chronicles.mainmenu.persona.switch");
			personaSwitch.getData().setDescription("message:chronicles.mainmenu.persona.switch.hint");
			personaSwitch.getData().setURI(appHelper.getPageURI(Switch.class));
			personaSwitch.setParentId(ChronicleConstants.NAV_PERSONA_ID); // Link to the "My Persona" Menu

		configuration.add("chro_personaSwitch", personaSwitch,"after:chro_personaSheet");		

    	// Add Mail Menu
		Node<NavMenuItem> mail = NodeFactory.getNode(ChronicleConstants.NAV_MAIL_ID);
			mail.setData(new StaticNavMenuItem(NavMenuItemType.STATIC_LIST));
			mail.getData().setLabel("message:chronicles.mainmenu.mail");
			mail.getData().setDescription("message:chronicles.mainmenu.mail.hint");
			mail.getData().setCss("icon-envelope icon-white");
			mail.setParentId(LayoutConstants.CHRO_TOP_LEFT_MENU_ID); // Link to the Main Chronicles Menu (Left)
		
		configuration.add("chro_mail", mail,"after:chro_persona");		
		
		// Add Menu Items to Mail Menu
		// Add Inbox Menu
		Node<NavMenuItem> inbox = NodeFactory.getNode();
			inbox.setData(new StaticNavMenuItem(NavMenuItemType.DEFAULT));
			inbox.getData().setMainPageId(Index.PAGE_ID);
			inbox.getData().setLabel("message:chronicles.mainmenu.mail.inbox");
			inbox.getData().setDescription("message:chronicles.mainmenu.mail.inbox.hint");
			inbox.getData().setURI(appHelper.getPageURI(org.libermundi.theorcs.chronicles.tapestry.pages.message.Box.class));
			inbox.setParentId(ChronicleConstants.NAV_MAIL_ID); // Link to the Mail Menu

		configuration.add("chro_mailInbox", inbox, "after:chro_mail");

		// Add Write Message Menu
		Node<NavMenuItem> writeMail = NodeFactory.getNode();
			writeMail.setData(new StaticNavMenuItem(NavMenuItemType.DEFAULT));
			writeMail.getData().setMainPageId(Message.PAGE_ID);
			writeMail.getData().setLabel("message:chronicles.mainmenu.mail.write");
			writeMail.getData().setDescription("message:chronicles.mainmenu.mail.write.hint");
			writeMail.getData().setURI(appHelper.getPageURI(Message.class));
			writeMail.setParentId(ChronicleConstants.NAV_MAIL_ID); // Link to the Mail Menu

		configuration.add("chro_mailWrite", writeMail, "after:chro_mailInbox");
		
		// Add Address Book Menu
		Node<NavMenuItem> addressBook = NodeFactory.getNode();
			addressBook.setData(new StaticNavMenuItem(NavMenuItemType.DEFAULT));
			addressBook.getData().setMainPageId(AddressBook.PAGE_ID);
			addressBook.getData().setLabel("message:chronicles.mainmenu.mail.addressbook");
			addressBook.getData().setDescription("message:chronicles.mainmenu.mail.addressbook.hint");
			addressBook.getData().setURI(appHelper.getPageURI(AddressBook.class));
			addressBook.setParentId(ChronicleConstants.NAV_MAIL_ID); // Link to the Mail Menu

		configuration.add("chro_mailAddressBook", addressBook, "after:chro_mailWrite");
		
		// Add Scene Menu
		Node<NavMenuItem> scenes = NodeFactory.getNode(ChronicleConstants.NAV_SCENE_ID);
			scenes.setData(new StaticNavMenuItem(NavMenuItemType.STATIC_LIST));
			scenes.getData().setLabel("message:chronicles.mainmenu.scenes");
			scenes.getData().setDescription("message:chronicles.mainmenu.scenes.hint");
			scenes.getData().setCss("icon-film icon-white");
			scenes.setParentId(LayoutConstants.CHRO_TOP_LEFT_MENU_ID); // Link to the Main Chronicles Menu (Left)

		configuration.add("chro_scenes", scenes, "after:chro_mail");
		
		// Add Infos Menu
		Node<NavMenuItem> infos = NodeFactory.getNode(ChronicleConstants.NAV_BACK_ID);
			infos.setData(new StaticNavMenuItem(NavMenuItemType.STATIC_LIST));
			infos.getData().setLabel("message:chronicles.mainmenu.infos");
			infos.getData().setDescription("message:chronicles.mainmenu.infos.hint");
			infos.getData().setCss("icon-list-alt icon-white");
			infos.setParentId(LayoutConstants.CHRO_TOP_LEFT_MENU_ID); // Link to the Main Chronicles Menu (Left)

		configuration.add("chro_infos", infos, "after:chro_scenes");
		
	}

	@SuppressWarnings("rawtypes")
	private static void createTopRightChronicleMenu(
			OrderedConfiguration<Node> configuration, Messages messages,
			LayoutAppHelper layoutHelper, AppHelper appHelper) {
		//Top Right Menu in Chronicle
    	
    	// Add the Search Form
    	Node<NavMenuItem> search = NodeFactory.getNode("CHRO_SEARCH_FIELD");
	    	search.setData(new StaticNavMenuItem(NavMenuItemType.SEARCHFORM));
	    	search.getData().setMainPageId("CHRO_SEARCH_FIELD");
	    	search.setParentId(LayoutConstants.CHRO_TOP_RIGHT_MENU_ID); // Link to the Main Chronicles Menu (Right)
    	
    	configuration.add("chro_search",search,"after:chro_persona");
		
    	// Add Profile Link
		Node<NavMenuItem> profile = NodeFactory.getNode();
			profile.setData(new StaticNavMenuItem(NavMenuItemType.ICON));
			profile.getData().setMainPageId(Profile.PAGE_ID);
			profile.getData().setDescription("message:chronicles.mainmenu.profile.hint");
			profile.getData().setCss("icon-user icon-white");
			profile.getData().setURI(appHelper.getPageURI(Profile.class));
			profile.setParentId(LayoutConstants.CHRO_TOP_RIGHT_MENU_ID); // Link to the Main Chronicles Menu (Right)

		configuration.add("chro_profile", profile,"after:chro_search");
		
		// Add Admin Link
		Node<NavMenuItem> admin = NodeFactory.getNode();
			admin.setData(new StaticNavMenuItem(NavMenuItemType.ICON));
			admin.getData().setMainPageId(Index.PAGE_ID);
			admin.getData().setDescription("message:chronicles.mainmenu.admin.hint");
			admin.getData().setCss("icon-lock icon-white");
			admin.getData().setURI(appHelper.getPageURI(Index.class));
			admin.addConstrain(new AclNodeConstrain(BasePermission.ADMINISTRATION));
			admin.setParentId(LayoutConstants.CHRO_TOP_RIGHT_MENU_ID); // Link to the Main Chronicles Menu (Right)

		configuration.add("chro_admin", admin, "after:chro_profile");
		
    	// Add the "Logout from chronicle" Link 
    	Node<NavMenuItem> logout = NodeFactory.getNode();
	    	logout.setData(new StaticNavMenuItem(NavMenuItemType.ICON));
	    	logout.getData().setMainPageId(Logout.PAGE_ID);
	    	logout.getData().setDescription("message:chronicles.mainmenu.logout.hint");
	    	logout.getData().setURI(appHelper.getPageURI(Logout.class));
	    	logout.getData().setCss("icon-off icon-white");
	    	logout.addConstrain(new RoleNodeConstrain(SecurityConstants.ROLE_USER));
	    	logout.setParentId(LayoutConstants.CHRO_TOP_RIGHT_MENU_ID); // Link to the Main Chronicles Menu (Left)
	    	
    	configuration.add("chro_logout", logout, "after:chro_admin");
		
	}
	
	@SuppressWarnings("rawtypes")
	private static void createChronicleAdminMenu(OrderedConfiguration<Node> configuration,
			Messages messages, LayoutAppHelper layoutHelper,
			AppHelper appHelper) {
		// Chronicle Administration Menu
		Node<NavMenuItem> chronicleAdminMenu = layoutHelper.buildStaticMenuNode(null, ChronicleConstants.CHRO_ADMIN_NAV_ID, "/");
			chronicleAdminMenu.setId(ChronicleConstants.CHRO_ADMIN_NAV_ID);
		
		configuration.add(ChronicleConstants.CHRO_ADMIN_NAV_ID, chronicleAdminMenu, "after:chro_admin");
		
		createPersonaManagementMenu(configuration,messages,layoutHelper,appHelper);

		createFactionAndGroupManagementMenu(configuration,messages,layoutHelper,appHelper);
	}
	
	@SuppressWarnings("rawtypes")
	private static void createFactionAndGroupManagementMenu(
			OrderedConfiguration<Node> configuration, Messages messages,
			LayoutAppHelper layoutHelper, AppHelper appHelper) {
		// Faction and Groups Management
		Node<NavMenuItem> adminFaction = NodeFactory.getNode();
			adminFaction.setId("AdminFactMenu");
			adminFaction.setData(new StaticNavMenuItem(NavMenuItemType.DEFAULT));
			adminFaction.getData().setLabel("message:chronicles.adminmenu.faction.label");
			adminFaction.getData().setDescription("message:chronicles.adminmenu.faction.description");
			adminFaction.addConstrain(new RoleNodeConstrain(SecurityConstants.ROLE_USER));
			adminFaction.addConstrain(new AclNodeConstrain(ChroniclePermission.FACTION));
			adminFaction.setParentId(ChronicleConstants.CHRO_ADMIN_NAV_ID); // Link to the Chronicles Admin Menu
		
	}

	@SuppressWarnings("rawtypes")
	private static void createPersonaManagementMenu(
			OrderedConfiguration<Node> configuration, Messages messages,
			LayoutAppHelper layoutHelper, AppHelper appHelper) {
		// Persona Management
		Node<NavMenuItem> adminPersona = NodeFactory.getNode();
			adminPersona.setId("AdminPersonaMenu");
			adminPersona.setData(new StaticNavMenuItem(NavMenuItemType.DEFAULT));
			adminPersona.getData().setLabel("message:chronicles.adminmenu.persona.label");
			adminPersona.getData().setDescription("message:chronicles.adminmenu.persona.description");
			adminPersona.addConstrain(new RoleNodeConstrain(SecurityConstants.ROLE_USER));
			adminPersona.addConstrain(new AclNodeConstrain(ChroniclePermission.PERSONA));
			adminPersona.setParentId(ChronicleConstants.CHRO_ADMIN_NAV_ID); // Link to the Chronicles Admin Menu
		
		configuration.add("chro_admin_perso", adminPersona, "after:"+ChronicleConstants.CHRO_ADMIN_NAV_ID);

		Node<NavMenuItem> mainManagement = NodeFactory.getNode();
			mainManagement.setData(new StaticNavMenuItem(NavMenuItemType.DEFAULT));
			mainManagement.getData().setMainPageId(org.libermundi.theorcs.chronicles.tapestry.pages.admin.perso.Main.PAGE_ID);
			mainManagement.getData().setLabel("message:chronicles.adminmenu.persona.main.label");
			mainManagement.getData().setDescription("message:chronicles.adminmenu.persona.main.description");
			mainManagement.getData().setURI(appHelper.getPageURI(org.libermundi.theorcs.chronicles.tapestry.pages.admin.perso.Main.class));
			mainManagement.setParentId("AdminPersonaMenu");
			
		configuration.add("chro_admin_perso_main", mainManagement, "after:chro_admin_perso");
		
		Node<NavMenuItem> sheetManagement = NodeFactory.getNode();
			sheetManagement.setData(new StaticNavMenuItem(NavMenuItemType.DEFAULT));
			sheetManagement.getData().setMainPageId(org.libermundi.theorcs.chronicles.tapestry.pages.admin.perso.Sheet.PAGE_ID);
			sheetManagement.getData().setLabel("message:chronicles.adminmenu.persona.sheet.label");
			sheetManagement.getData().setDescription("message:chronicles.adminmenu.persona.sheet.description");
			sheetManagement.getData().setURI(appHelper.getPageURI(org.libermundi.theorcs.chronicles.tapestry.pages.admin.perso.Sheet.class));
			sheetManagement.setParentId("AdminPersonaMenu");
			
		configuration.add("chro_admin_perso_sheet", sheetManagement, "after:chro_admin_perso_infos");
	
		Node<NavMenuItem> rumorsManagement = NodeFactory.getNode();
			rumorsManagement.setData(new StaticNavMenuItem(NavMenuItemType.DEFAULT));
			rumorsManagement.getData().setMainPageId(org.libermundi.theorcs.chronicles.tapestry.pages.admin.perso.Rumors.PAGE_ID);
			rumorsManagement.getData().setLabel("message:chronicles.adminmenu.persona.rumors.label");
			rumorsManagement.getData().setDescription("message:chronicles.adminmenu.persona.rumors.description");
			rumorsManagement.getData().setURI(appHelper.getPageURI(org.libermundi.theorcs.chronicles.tapestry.pages.admin.perso.Rumors.class));
			rumorsManagement.setParentId("AdminPersonaMenu");
			
		configuration.add("chro_admin_perso_rumors", rumorsManagement, "after:chro_admin_perso_sheet");
			
		Node<NavMenuItem> contactManagement = NodeFactory.getNode();
			contactManagement.setData(new StaticNavMenuItem(NavMenuItemType.DEFAULT));
			contactManagement.getData().setMainPageId(org.libermundi.theorcs.chronicles.tapestry.pages.admin.perso.Contacts.PAGE_ID);
			contactManagement.getData().setLabel("message:chronicles.adminmenu.persona.contacts.label");
			contactManagement.getData().setDescription("message:chronicles.adminmenu.persona.contacts.description");
			contactManagement.getData().setURI(appHelper.getPageURI(org.libermundi.theorcs.chronicles.tapestry.pages.admin.perso.Contacts.class));
			contactManagement.setParentId("AdminPersonaMenu");
			
		configuration.add("chro_admin_perso_contacts", contactManagement, "after:chro_admin_perso_rumors");		
		
	}
	
	
	@Advise(id="AclChronicleContrainMethodAdvice",serviceInterface=MenuService.class)
	public static void filterMenuServiceResults(MethodAdviceReceiver receiver,
				AnnotatedMethodAdvisor annotatedMethodAdvisor, 
				final SecurityManager securityManager, 
				final ApplicationStateManager applicationStateManager) {
		
		// AclChronicleConstrain Advisor
		MethodAdvice aclma = new AclChronicleContrainMethodAdvice(securityManager, applicationStateManager);
		annotatedMethodAdvisor.addAdvice(aclma, receiver);
	}	
}
